package com.aliyun.messagemgt.web.aop;

import com.alibaba.cloudapi.sdk.core.model.ApiResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.messagemgt.common.annotation.CustomAnnotation;
import com.aliyun.messagemgt.common.annotation.OperateLogAnnotation;
import com.aliyun.messagemgt.application.service.ResourceNameService;
import com.aliyun.messagemgt.common.base.NullResouce;
import com.aliyun.messagemgt.util.SyncApiListClient;
import com.aliyun.messagemgt.common.dto.base.UserInfo;
import com.aliyun.messagemgt.common.constants.MgtCommonConst;
import com.aliyun.messagemgt.dto.LogAudit;
import com.aliyun.messagemgt.dto.OperateLog;
import com.aliyun.messagemgt.dto.vo.ConditionVO;
import com.aliyun.messagemgt.domain.repository.OperateLogRepository;
import com.aliyun.messagemgt.common.enums.ResourceTypeEnum;
import com.aliyun.messagemgt.common.dto.response.AjaxResult;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created with IDEA
 * 操作日志aop 在controller层使用
 *
 * @author:zhangxingxing 0027012120
 * @date:2019/3/15
 * @time:17:44
 */
//@Configuration
//@Aspect
//@Order(1)
public class OperateLogAop {

    private static final Logger logger = LoggerFactory.getLogger(OperateLogAop.class);
    public static List<LogAudit.LogContents> logContents = new ArrayList<>();
    @Resource
    private OperateLogRepository operateLogRepository;
    @Autowired
    private ResourceNameService resourceNameService;
    @Autowired
    private HttpServletRequest request;

    /**
     * 处理流程：
     * 读取注解中定义的资源名 和 操作
     * 读取参数中添加的用户信息
     * 插入操作日志表
     *
     * @param joinPoint
     * @param rvt
     */
    @AfterReturning(returning = "rvt", pointcut = "@annotation(com.aliyun.messagemgt.common.annotation.OperateLogAnnotation)")
    public void afterReturning(JoinPoint joinPoint, Object rvt) {
        logger.info("OperateLogAop insert Log");
        Object[] params = joinPoint.getArgs();
        UserInfo userInfo = null;
        for (int i = 0; i < params.length; i++) {
            if (params[i] instanceof UserInfo) {
                userInfo = (UserInfo) params[i];
                break;
            }
        }
        // 如果没有UserInfo的类，证明没有按规范继承
        if (userInfo == null) {
            logger.error("{} 参数未按要求继承UserInfo", joinPoint.getSignature());
            return;
        }
        // 获取注解中的信息
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        OperateLogAnnotation operateLogAnnotation = method.getAnnotation(OperateLogAnnotation.class);

        AjaxResult result = (AjaxResult) rvt;
        boolean success = result.isSuccess();
        OperateLog operateLog = new OperateLog()
                .setOperateType(operateLogAnnotation.operateType().getCode())
                .setOperateResourceType(operateLogAnnotation.resouceType().getCode())
                .setOperateUser(userInfo.getUserId())
                .setOperateUserName(userInfo.getUserName())
                .setOperateDept(userInfo.getDepartment())
                .setOperateDeptName(userInfo.getDepartmentName())
                .setOperateTime(new Date());

        // 处理module, 目前逻辑暂时是resourceType为消息源时，消息属于消息中心
        if (ResourceTypeEnum.MSGSOURCE.getCode().equals(operateLog.getOperateResourceType())) {
            operateLog.setModule(MgtCommonConst.MODULE_MSG_CENTER);
        } else {
            operateLog.setModule(MgtCommonConst.MODULE_SMART_PUSH);
        }

        // 获取注解中所定义的资源类
        Class clazz = operateLogAnnotation.resouceClass();
        Object clazzParam = null;
        // 如果定义了资源的具体信息  需要去找出操作资源的名称
        if (clazz != NullResouce.class) {
            Object param = null;
            for (int i = 0; i < params.length; i++) {
                if (params[i].getClass() == clazz) {
                    param = params[i];
                    break;
                }
            }
            if (param == null) {
                logger.error("注解中 找不到对应的class {} ", clazz);
                return;
            }
            // 获取注解中定义的ID字段 field
            Field field = null;
            try {
                field = clazz.getDeclaredField(operateLogAnnotation.resouceIdField());
            } catch (NoSuchFieldException e) {
                logger.error("{} 中找不到对应的属性 {} ", clazz, operateLogAnnotation.resouceIdField());
                return;
            }
            // 获取field中的实际值
            field.setAccessible(true);
            Integer resourceId = null;
            try {
                // 资源的ID均为int类型所以用getInt
                resourceId = (Integer) field.get(param);
                clazzParam = param;
            } catch (IllegalAccessException e) {
                logger.error("{} 获取ID值异常 {} ", clazz, operateLogAnnotation.resouceIdField());
                return;
            }

            if (resourceId == null) {
                logger.error("ID值为空，未注入 {} ", resourceId);
                return;
            }

            operateLog.setOperateResourceId(resourceId)
                    .setOperateResourceName(resourceNameService.findResourceName(resourceId, operateLogAnnotation.resouceType()));
        }
        if (success) {
            operateLog.setOperateStatus(1);
        } else {
            operateLog.setOperateStatus(0).setFailMsg(result.getErrMsg());
        }
        operateLogRepository.insertOperateLog(operateLog);
        //日志审计处理
        logAuditHandle(operateLog, userInfo, result, clazz, clazzParam);
        //置空
        clazzParam = null;
    }

    /**
     * 日志审计处理
     *
     * @param operateLog
     * @param userInfo
     * @param result
     */
    public void logAuditHandle(OperateLog operateLog, UserInfo userInfo, AjaxResult result, Class cl, Object param) {
        LogAudit.LogContents logContents = new LogAudit.LogContents();
        //审批中心字段
        logContents.setNeedApprove(0);
        logContents.setBusinessId(UUID.randomUUID().toString().replace("-", "").toLowerCase());
        logContents.setBusinessTime((System.currentTimeMillis() / 1000) + "");
        logContents.setServiceAddress(request.getRequestURL().toString());
        String userName = userInfo.getUserName();
        String userId = userInfo.getUserId();

        if (StringUtils.isEmpty(userId)) {
            userId = "hzos-xxzx";
            userName = "hzos-xxzx";
        }
        logContents.setUserId(userId);
        logContents.setDataId("hzga-00025");
        logContents.setUserName(userName);
        //处理角色名称
        handleUserRoleName(userInfo, logContents);
        logger.info("单位名称：{}", userInfo.getDepartmentName());
        logger.info("单位机构代码：{}", userInfo.getDepartment());
        logContents.setOrganization(userInfo.getDepartmentName());
        logContents.setOrganizationId(userInfo.getDepartment());
        //终端标识
        String terminalId = getIpAddress(request);
        logContents.setTerminalId(terminalId);
        logContents.setTerminalIp(terminalId);
        logContents.setTerminalType("0");
        //操作类型 1：保存；2：提交；3：修改；4：删除
        //2020年3月9日改动：1-查询 2-新增 3-更新 4-删除
        logContents.setOperateType(transformCode(operateLog.getOperateType()));
        //用户操作的结果，包括成功/失败。0:成功；其他：失败
        logger.info("用户操作的结果:" + result.getErrCode());
        if (result.getErrCode() == 0L) {
            logContents.setOperateResult("1");
        } else {
            logContents.setOperateResult("0");
        }
        logger.info("OperateResult:" + logContents.getOperateResult());
        //操作类型为0-登录时，置空；为其它类型时，可填写当时用户所操作的具体功能模块名称
        logContents.setOperateName("杭州消息中心");
        logContents.setOperateObject(operateLog.getOperateResourceName());
        handleOperateConditionAndDesc(logContents, cl, param);
        LogAudit logQuery = new LogAudit();
        //b05701d48cd9724cbfb601417f862866
        logQuery.setSysId("053981968a5604999e737782150920ec");
        logQuery.setLogType("010");
        List<String> logContentlist = new ArrayList<>();
        logContentlist.add(JSON.toJSONString(logContents));
        logQuery.setLogContents(logContentlist);
        //校验码
        String checkSum = OperateLogAop.getCheckSum(logQuery);
        logQuery.setCheckSum(checkSum);
        auditLogUpload(logQuery);
        OperateLogAop.logContents.clear();
    }

    /**
     * 操作条件与描述封装
     *
     * @param logContents
     * @param cl
     */
    public static void handleOperateConditionAndDesc(LogAudit.LogContents logContents, Class cl, Object param) {
        List<ConditionVO> list = new ArrayList<>();
        StringBuffer operateConditionDesc = new StringBuffer();
        Field[] fields = cl.getDeclaredFields();
        for (Field fl : fields) {
            fl.setAccessible(true);
            ConditionVO conditionVO = new ConditionVO();
            try {
                //获取字段描述
                Annotation[] annotations = fl.getDeclaredAnnotations();
                String desc = "";
                for (Annotation annotation : annotations) {
                    if (annotation instanceof CustomAnnotation) {
                        conditionVO.setFieldName(fl.getName());
                        Object object = fl.get(param);
                        conditionVO.setFieldValue(JSONObject.toJSONString(object));
                        list.add(conditionVO);
                        CustomAnnotation customAnnotation = (CustomAnnotation) annotation;
                        logger.info("name: " + customAnnotation.name());
                        //获取字段描述
                        logger.info("desc: " + customAnnotation.desc());
                        desc = customAnnotation.desc();
                    }
                }
                if (StringUtils.isNotBlank(desc)) {
                    operateConditionDesc.append(desc).append("=").append(conditionVO.getFieldValue()).append(",");
                }
                conditionVO.setFieldNameDesc(desc);
                conditionVO.setFieldValueDesc(conditionVO.getFieldValue());
            } catch (IllegalAccessException e) {
                logger.info("异常", e);
            }
        }
        logContents.setOperateCondition(JSONObject.toJSONString(list));
        String str = operateConditionDesc.toString();
        String suffix = ",";
        if (str.endsWith(suffix)) {
            str = str.substring(0, operateConditionDesc.toString().length() - 1);
        }
        logContents.setOperateConditionDesc(str);
    }

    /**
     * 处理用户角色
     *
     * @param userInfo
     * @param logContents
     */
    public void handleUserRoleName(UserInfo userInfo, LogAudit.LogContents logContents) {
        if (userInfo.getRole() > 0L) {
            //用户角色 1 超级管理员  2 数据管理员  3 普通用户
            String roleName = "";
            switch (userInfo.getRole()) {
                case 1:
                    roleName = "超级管理员";
                    break;
                case 2:
                    roleName = "数据管理员";
                    break;
                case 3:
                    roleName = "普通用户";
                    break;
                default:
                    roleName = "普通用户";
                    break;
            }
            logContents.setUserRole(roleName);
        } else {
            logContents.setUserRole("");
        }
    }

    /**
     * 转换为三所定义的标准编码
     * 2020年3月9日改动：1-查询 2-新增 3-更新 4-删除
     * @param code code
     * @return string
     */
    private Integer transformCode(Integer code) {
        Integer standard = null;
        switch (code) {
            case 1:
                standard = 2;
                break;
            case 2:
            case 3:
                standard = 3;
                break;
            case 4:
                standard = 4;
                break;
            default:
                standard = 1;
                break;
        }
        return standard;
    }

    /**
     * 获取校验码
     *
     * @throws
     * @Author: lby
     * @Date: 2019-12-24
     */
    public static String getCheckSum(LogAudit logQuery) {
        StringBuffer sb = new StringBuffer();
        sb.append(logQuery.getSysId()).append("&").append(logQuery.getLogType()).append("&")
                .append(JSON.toJSONString(logQuery.getLogContents()));
        logger.info("审计接口checksum值：{}" + sb.toString());
        logger.info("审计接口checksumMD5结果：{}" + DigestUtils.md5Hex(sb.toString()));
        return DigestUtils.md5Hex(sb.toString());
    }

    /**
     * 获取请求IP
     *
     * @param request
     * @return
     */
    public String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        String strUnKnown = "unknown";
        if (ip != null && ip.length() != 0 && !strUnKnown.equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            String str = ",";
            if (ip.contains(str)) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || strUnKnown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
            logger.info("Proxy-Client-IP ip: {}",ip);
        }
        if (ip == null || ip.length() == 0 || strUnKnown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
            logger.info("WL-Proxy-Client-IP ip: {}", ip);
        }
        if (ip == null || ip.length() == 0 || strUnKnown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
            logger.info("HTTP_CLIENT_IP ip: {}" , ip);
        }
        if (ip == null || ip.length() == 0 || strUnKnown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            logger.info("HTTP_X_FORWARDED_FOR ip: {}",ip);
        }
        if (ip == null || ip.length() == 0 || strUnKnown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
            logger.info("X-Real-IP ip: {}" ,ip);
        }
        if (ip == null || ip.length() == 0 || strUnKnown.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            logger.info("getRemoteAddr ip: {}", ip);
        }
        logger.info("ip----:{} ", ip);
        return ip;
    }

    /**
     * 日志审计功能
     *
     * @Author: lby
     * @Date: 2019-12-24
     */
    public JSONObject auditLogUpload(LogAudit logQuery) {
        logger.info("调用日志审计接口begin");
        String apiUrl = "/sjpt/auditcenter/services/sjzx_logupload";
        String apiHost = "306a19b0fc614a9791cdbc2cedce69ae.apigateway.cn-deqing-zjzfy01-zjga01.hzs.zj";
        String apiAppKey = "1562932588685601";
        String apiAppSecret = "4d366746f3914be097b6c211b51c9e63";
        Map<String, String> headerParams = new HashMap<>(16);
        Map<String, String> formParams = new HashMap<>(16);
        // 创建连接
        SyncApiListClient syncClient = SyncApiListClient.newBuilder().appKey(apiAppKey).appSecret(apiAppSecret)
                .connectionTimeoutMillis(30000L).readTimeoutMillis(30000L).writeTimeoutMillis(30000L).build();
        logger.info("logQuery:{}", JSONObject.toJSONString(logQuery));
        ApiResponse apiResponse = null;
        try {
            apiResponse = syncClient.apiList(formParams, headerParams, apiUrl, apiHost, JSONObject.toJSONString(logQuery).getBytes());
            // 响应状态码，大于等于200小于300表示成功；大于等于400小于500为客户端错误；大于500为服务端错误。
            logger.info("审计接口处理返回数据======>: {}", apiResponse);
            logger.info(" 调用IT服务平台返回结果 {}", apiResponse.getBody());
        } catch (Exception e) {
            logger.error("调用IT服务平台报错:{}", e.getMessage());
        }
        logger.info("调用日志审计接口end");
        return null;
    }


}
